package com.mgg.payment.service.weixin;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

import com.mgg.common.dto.OrderDTO;
import com.mgg.common.dto.RefundOrderDTO;
import com.mgg.common.exception.ServiceException;
import com.mgg.payment.config.WxPayAccountConfig;
import com.mgg.payment.constants.WxPayConstants;
import com.mgg.payment.enums.PaymentResultEnum;
import com.mgg.payment.model.PayResponse;
import com.mgg.payment.model.wxpay.WxPayApi;
import com.mgg.payment.model.wxpay.WxPayResponse;
import com.mgg.payment.request.WxpayTradeAppPayRequest;
import com.mgg.payment.request.WxpayTradeRefundRequest;
import com.mgg.payment.response.WxpayAsyncNotifyResponse;
import com.mgg.payment.response.WxpayTradeAppPayResponse;
import com.mgg.payment.response.WxpayTradeRefundPayResponse;
import com.mgg.payment.util.MoneyUtils;
import com.mgg.payment.util.RandomUtils;
import com.mgg.payment.util.XmlUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.alipay.api.AlipayApiException;

import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.RequestBody;
import retrofit2.Call;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.simplexml.SimpleXmlConverterFactory;

@Service("wxPayService")
public class WxPayService {

	private Logger log = LoggerFactory.getLogger(getClass());
	
	@Autowired
	private WxPayAccountConfig wxPayAccountConfig;
	
	/**
     * 预支付
     * @param orderId
     * @param totalAmount
     * @return
     * @throws AlipayApiException
     */
    public WxPayResponse create(String orderId, BigDecimal totalAmount) throws Exception {
    	WxpayTradeAppPayRequest request = new WxpayTradeAppPayRequest();
    	request.setOutTradeNo(orderId);
    	request.setTotalFee(MoneyUtils.Yuan2Fen(totalAmount.doubleValue()));
    	request.setBody("美乖乖");
    	request.setOpenid(request.getOpenid());

    	request.setTradeType("APP");
    	request.setAppid(wxPayAccountConfig.getAppId());
    	request.setMchId(wxPayAccountConfig.getMchId());
    	request.setNotifyUrl(wxPayAccountConfig.getNotifyUrl());
    	request.setNonceStr(RandomUtils.getRandomStr());
    	request.setSpbillCreateIp(request.getSpbillCreateIp() == null || request.getSpbillCreateIp().isEmpty() ? "8.8.8.8" : request.getSpbillCreateIp());
    	request.setSign(WxPaySignature.sign(buildMap(request), wxPayAccountConfig.getMchKey()));
        
    	Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(WxPayConstants.WXPAY_GATEWAY)
                .addConverterFactory(SimpleXmlConverterFactory.create())
                .build();
        String xml = XmlUtils.toXMl(request);
        RequestBody body = RequestBody.create(MediaType.parse("application/xml; charset=utf-8"),xml);
        Call<WxpayTradeAppPayResponse> call = retrofit.create(WxPayApi.class).unifiedorder(body);
        Response<WxpayTradeAppPayResponse> retrofitResponse  = null;
        try{
            retrofitResponse = call.execute();
        }catch (IOException e) {
            e.printStackTrace();
        }
        if (!retrofitResponse.isSuccessful()) {
        	log.error("【微信统一支付】发起支付, 网络异常");
            throw new ServiceException(PaymentResultEnum.WXPAY_UNIFIEDORDER_ERROR.getCode(),PaymentResultEnum.WXPAY_UNIFIEDORDER_ERROR.getMessage());
        }
        WxpayTradeAppPayResponse response = retrofitResponse.body();

        if(!response.getReturnCode().equals(WxPayConstants.SUCCESS)) {
        	log.error("【微信统一支付】发起支付, returnCode != SUCCESS, returnMsg = " + response.getReturnMsg());
            throw new ServiceException(PaymentResultEnum.WXPAY_UNIFIEDORDER_ERROR.getCode(),PaymentResultEnum.WXPAY_UNIFIEDORDER_ERROR.getMessage());
        }
        if (!response.getResultCode().equals(WxPayConstants.SUCCESS)) {
        	log.error("【微信统一支付】发起支付, resultCode != SUCCESS, err_code = " + response.getErrCode() + " err_code_des=" + response.getErrCodeDes());
            throw new ServiceException(PaymentResultEnum.WXPAY_UNIFIEDORDER_ERROR.getCode(),PaymentResultEnum.WXPAY_UNIFIEDORDER_ERROR.getMessage());
        }
        return buildPayResponse(response);
    }
    
    public PayResponse notify(String notifyData, String mchKey) {
    	//xml解析为对象
    	WxpayAsyncNotifyResponse asyncResponse = (WxpayAsyncNotifyResponse) XmlUtils.fromXML(notifyData, WxpayAsyncNotifyResponse.class);

        //签名校验
        if (!WxPaySignature.verify(buildMap(asyncResponse),mchKey)) {
            log.error("【微信支付】异步通知, 签名验证失败, response={}", asyncResponse);
            throw new ServiceException(PaymentResultEnum.WXPAY_NOTIFY_ERROR.getCode(), PaymentResultEnum.WXPAY_NOTIFY_ERROR.getMessage());
        }

        if(!asyncResponse.getReturnCode().equals(WxPayConstants.SUCCESS)) {
        	log.error("【微信支付】异步通知, returnCode != SUCCESS, returnMsg = " + asyncResponse.getReturnMsg());
            throw new ServiceException(PaymentResultEnum.WXPAY_NOTIFY_ERROR.getCode(), PaymentResultEnum.WXPAY_NOTIFY_ERROR.getMessage());
        }
        //该订单已支付直接返回
        if (!asyncResponse.getResultCode().equals(WxPayConstants.SUCCESS)
                && asyncResponse.getErrCode().equals("ORDERPAID")) {
            return buildPayResponse(asyncResponse);
        }

        if (!asyncResponse.getResultCode().equals(WxPayConstants.SUCCESS)) {
        	log.error("【微信支付】异步通知, resultCode != SUCCESS, err_code = " + asyncResponse.getErrCode() + " err_code_des=" + asyncResponse.getErrCodeDes());
        	throw new ServiceException(PaymentResultEnum.WXPAY_NOTIFY_ERROR.getCode(), PaymentResultEnum.WXPAY_NOTIFY_ERROR.getMessage());
        }

        return buildPayResponse(asyncResponse);
    }
    
    private PayResponse buildPayResponse(WxpayAsyncNotifyResponse response) {
    	PayResponse payResponse = new PayResponse();
        payResponse.setOrderAmount(MoneyUtils.Fen2Yuan(response.getTotalFee()));
        payResponse.setOrderId(response.getOutTradeNo());
        payResponse.setOutTradeNo(response.getTransactionId());
        return payResponse;
	}
    
	/**
     * 
     * @param response
     * @return
     */
    private WxPayResponse buildPayResponse(WxpayTradeAppPayResponse response) {
    	String appId = response.getAppid();
    	String partnerid = response.getMchId();
    	String prepayid = response.getPrepayId();
    	String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
        String nonceStr = RandomUtils.getRandomStr();
        String packAge = "Sign=WXPay";
        
        //先构造要签名的map
        Map<String, String> map = new HashMap<>();
        map.put("appid", appId);
        map.put("noncestr", nonceStr);
        map.put("package", packAge);
        map.put("partnerid", partnerid);
        map.put("prepayid", prepayid);
        map.put("timestamp", timeStamp);
        
        WxPayResponse payResponse = new WxPayResponse();
        payResponse.setAppId(appId);
        payResponse.setPartnerId(partnerid);
        payResponse.setPrepayId(prepayid);
        payResponse.setTimeStamp(timeStamp);
        payResponse.setNonceStr(nonceStr);
        payResponse.setPackAge(packAge);
        payResponse.setSign(WxPaySignature.sign(map, wxPayAccountConfig.getMchKey()));
        
        return payResponse;

	}


	/**
     * 构造map
     * @param request
     * @return
     */
    private Map<String, String> buildMap(WxpayTradeAppPayRequest request) {
        Map<String, String> map = new HashMap<>();
        map.put("appid", request.getAppid());
        map.put("mch_id", request.getMchId());
        map.put("nonce_str", request.getNonceStr());
        map.put("sign", request.getSign());
        map.put("attach", request.getAttach());
        map.put("body", request.getBody());
        map.put("detail", request.getDetail());
        map.put("notify_url", request.getNotifyUrl());
        map.put("openid", request.getOpenid());
        map.put("out_trade_no", request.getOutTradeNo());
        map.put("spbill_create_ip", request.getSpbillCreateIp());
        map.put("total_fee", String.valueOf(request.getTotalFee()));
        map.put("trade_type", request.getTradeType());
        return map;
    }
    
    private Map<String, String> buildMap(WxpayAsyncNotifyResponse response) {
        Map<String, String> map = new HashMap<>();
        map.put("return_code", response.getReturnCode());
        map.put("return_msg", response.getReturnMsg());
        map.put("appid", response.getAppid());
        map.put("mch_id", response.getMchId());
        map.put("device_info", response.getDeviceInfo());
        map.put("nonce_str", response.getNonceStr());
        map.put("sign", response.getSign());
        map.put("result_code", response.getResultCode());
        map.put("err_code", response.getErrCode());
        map.put("err_code_des", response.getErrCodeDes());
        map.put("openid", response.getOpenid());
        map.put("is_subscribe", response.getIsSubscribe());
        map.put("trade_type", response.getTradeType());
        map.put("bank_type", response.getBankType());
        map.put("total_fee", String.valueOf(response.getTotalFee()));
        map.put("fee_type", response.getFeeType());
        map.put("cash_fee", response.getCashFee());
        map.put("cash_fee_type", response.getCashFeeType());
        map.put("coupon_fee", response.getCouponFee());
        map.put("coupon_count", response.getCouponCount());
        map.put("transaction_id", response.getTransactionId());
        map.put("out_trade_no", response.getOutTradeNo());
        map.put("attach", response.getAttach());
        map.put("time_end", response.getTimeEnd());
        return map;
    }

    /**
     * 退款
     * @return
     */
	public WxpayTradeRefundPayResponse refund(RefundOrderDTO refundOrder, OrderDTO order) {
		
		WxpayTradeRefundRequest wxRequest = new WxpayTradeRefundRequest();
		
        wxRequest.setOutTradeNo(order.getOrderId());
        wxRequest.setOutRefundNo(refundOrder.getRefundNo());
        wxRequest.setTotalFee(MoneyUtils.Yuan2Fen(order.getOrderTotal().doubleValue()));
        wxRequest.setRefundFee(MoneyUtils.Yuan2Fen(refundOrder.getAmount().doubleValue()));

        wxRequest.setAppid(wxPayAccountConfig.getAppId());
        wxRequest.setMchId(wxPayAccountConfig.getMchId());
        wxRequest.setNonceStr(RandomUtils.getRandomStr());
        wxRequest.setSign(WxPaySignature.sign(buildMap(wxRequest), wxPayAccountConfig.getMchKey()));
        
        //初始化证书
        if (wxPayAccountConfig.getSslContext() == null) {
        	wxPayAccountConfig.initSSLContext();
        }
        OkHttpClient.Builder okHttpClient = new OkHttpClient()
                .newBuilder()
                .sslSocketFactory(wxPayAccountConfig.getSslContext().getSocketFactory());

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(WxPayConstants.WXPAY_GATEWAY)
                .addConverterFactory(SimpleXmlConverterFactory.create())
                .client(okHttpClient.build())
                .build();
        String xml = XmlUtils.toXMl(wxRequest);
        RequestBody body = RequestBody.create(MediaType.parse("application/xml; charset=utf-8"),xml);
        Call<WxpayTradeRefundPayResponse> call = retrofit.create(WxPayApi.class).refund(body);
        Response<WxpayTradeRefundPayResponse> retrofitResponse  = null;
        try{
            retrofitResponse = call.execute();
        }catch (IOException e) {
            e.printStackTrace();
        }
        if (!retrofitResponse.isSuccessful()) {
        	log.error("【微信退款】发起退款, 网络异常, orderId = {}, refundNo = {}", order.getOrderId(), refundOrder.getRefundNo());
            throw new RuntimeException("【微信退款】发起退款, 网络异常");
        }
        WxpayTradeRefundPayResponse response = retrofitResponse.body();

        if(!response.getReturnCode().equals(WxPayConstants.SUCCESS)) {
        	log.error("【微信退款】发起退款, returnCode != SUCCESS, orderId = {}, refundNo = {}, returnMsg = {}", order.getOrderId(), refundOrder.getRefundNo(), response.getReturnMsg());
        	throw new RuntimeException("【微信退款】发起退款, returnCode != SUCCESS, returnMsg = " + response.getReturnMsg());
        }
        if (!response.getResultCode().equals(WxPayConstants.SUCCESS)) {
        	log.error("【微信退款】发起退款, 网络异常, orderId = {}, refundNo = {}, err_code = {} ", order.getOrderId(), refundOrder.getRefundNo());
            throw new RuntimeException("【微信退款】发起退款, resultCode != SUCCESS, err_code = " + response.getErrCode() + " err_code_des=" + response.getErrCodeDes());
        }

//      log.info("【微信退款】发起退款, response={}", JsonUtil.toJson(retrofitResponse.body()));
        return response;
	}
	
	private Map<String, String> buildMap(WxpayTradeRefundRequest request) {
        Map<String, String> map = new HashMap<>();
        map.put("appid", request.getAppid());
        map.put("mch_id", request.getMchId());
        map.put("nonce_str", request.getNonceStr());
        map.put("sign", request.getSign());
        map.put("sign_type", request.getSignType());
        map.put("transaction_id", request.getTransactionId());
        map.put("out_trade_no", request.getOutTradeNo());
        map.put("out_refund_no", request.getOutRefundNo());
        map.put("total_fee", String.valueOf(request.getTotalFee()));
        map.put("refund_fee", String.valueOf(request.getRefundFee()));
        map.put("refund_fee_type", request.getRefundFeeType());
        map.put("refund_desc", request.getRefundDesc());
        map.put("refund_account", request.getRefundAccount());
        return map;
    }
}
